package be.kuleuven.mume.query;

import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.jdo.JDOHelper;
import javax.jdo.ObjectState;
import javax.jdo.PersistenceManager;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import be.kuleuven.mume.PMF;
import be.kuleuven.mume.beans.PersoonBean;
import be.kuleuven.mume.exceptions.UserNotLoggedInException;

import com.google.appengine.api.datastore.Key;
import com.google.appengine.api.datastore.KeyFactory;
import com.google.appengine.api.users.User;
import com.google.appengine.api.users.UserService;
import com.google.appengine.api.users.UserServiceFactory;

public class PersoonQuery{

	private static Logger log = Logger.getLogger(PersoonQuery.class.getName());
	
	public static void store(PersoonBean pers){
		PersistenceManager pm = PMF.getPersistenceManager();
		pm.makePersistent(pers);
		pm.flush();
		pm.close();
	}
	
	public static boolean delete(String persId) {
		DeleteQueryHelper h = new DeleteQueryHelper(persId, PersoonBean.class);
		return h.executeAtomicQuery();
	}
	
	/**
	 * Add vakId to this person.
	 * @param vakId the vak that needs to be added.
	 * @result returns false if vak is already added.
	 */
	public static boolean addVak(String persoonId, final String vakId){
		if(VakQuery.getVak(vakId) == null) {
			Key key = KeyFactory.stringToKey(vakId);
			log.log(Level.SEVERE, "EntityNotFoundException", "Cannof find vakBean with Key" + key.toString());
			return false;
		}
		UpdateQueryHelper<PersoonBean> helper = new UpdateQueryHelper<PersoonBean>(persoonId) {
			
			@Override
			public Boolean doUpdate(PersoonBean objectToUpdate) {
				objectToUpdate.addVak(vakId);
				return true;
			}
		};
		return helper.executeAtomicQuery();
	}
	
	public static boolean removeVak(String persoonId, final String vakId){
		if(!checkPersoon(persoonId, true))
			return false;
		
		UpdateQueryHelper<PersoonBean> h = new UpdateQueryHelper<PersoonBean>(persoonId) {
			
			@Override
			public Boolean doUpdate(PersoonBean objectToUpdate) {
				return objectToUpdate.removeVak(vakId);
			}
		};
		return h.executeAtomicQuery();
	}
	
	public static PersoonBean getCurrentPersoon() throws UserNotLoggedInException{
		return getCurrentPersoon("");
	}
	/**
	 * Returns the PersoonBean of the current user.
	 * If this is the first time the user has logged in a new PersoonBean
	 * will be added to the database.
	 * 
	 * @param pm PersistenceManager is needed if you want to change Persoon. User getCurrentPersoon(String requestedUrl) if you are not going to change it. 
	 * @return PersoonBean object of the current logged in user.
	 * @throws UserNotLoggedInException 
	 */
	public static PersoonBean getCurrentPersoon(String requestedUrl) throws UserNotLoggedInException{
		return getCurrentPersoonQuery(requestedUrl, true).executeAtomicQuery();
	}

	public static QueryHelper<PersoonBean> getCurrentPersoonQuery(String requestedUrl, boolean autoclose) throws UserNotLoggedInException {
		UserService userService = UserServiceFactory.getUserService();
		final User user = userService.getCurrentUser();
		
		if(user == null)
			throw new UserNotLoggedInException(userService.createLoginURL(requestedUrl));
		
		final String googleId = user.getUserId();
		final Key key = KeyFactory.createKey(PersoonBean.class.getSimpleName(), googleId);
		
		QueryHelper<PersoonBean> helper = new QueryHelper<PersoonBean>() {
			
			@Override
			public PersoonBean execute(PersistenceManager pm) {
				PersoonBean p = null;
				
				//If existing use object Persoon from db.
				try {
					p = pm.getObjectById(PersoonBean.class, key);
				} catch (Exception e) {
					p=null;
					log.log(Level.WARNING, "New Persoon will be added to the db");
				}
				ObjectState state = JDOHelper.getObjectState(p);
				if(state==null||state.equals(ObjectState.PERSISTENT_DELETED)||state.equals(ObjectState.PERSISTENT_NEW_DELETED)) {
					p = null;
					log.log(Level.WARNING, "Person was deleted. Will be recreated");
				}

				if (p == null) {
					p = new PersoonBean();
					p.setId(key);//Set GoogleId as PK
					p.setUsername(user.getNickname());//User google nick as temporal username
					pm.makePersistent(p);
				}
				return p;
			}
		};
		helper.autoCloseQuery = autoclose;
		return helper;
	}
	
	public static PersoonBean getCurrentPersoon(PersistenceManager pm,
			HttpServletRequest req, HttpServletResponse resp, boolean b) throws IOException {
		try{
			return getCurrentPersoon(req.getRequestURI());
		} catch(UserNotLoggedInException e) {
			resp.sendRedirect(e.loginUrl);
			return null;
		}
	}

	public static PersoonBean getPersoon(String persoonId) {
		FetchQueryHelper<PersoonBean> helper = new FetchQueryHelper<PersoonBean>(persoonId){};
		return helper.executeAtomicQuery();
	}
	
	public static boolean checkPersoon(String persoonId, boolean printlog) {
		PersoonBean p = PersoonQuery.getPersoon(persoonId);
		if(p==null) {
			if(printlog){
				Key key = KeyFactory.stringToKey(persoonId);
				log.log(Level.SEVERE, "EntityNotFoundException", 
						(new Exception("Cannot find PersoonBean with key" + key.toString())));
			}
			return false;
		}
		return true;
	}
}
